/**
 * UniRequest 核心实现
 */

import type {
    DownloadConfig,
    DownloadProgress,
    DownloadResponse,
    UniRequestConfig,
    UniRequestInstance,
    UniRequestInternalConfig,
    UniRequestUserConfig,
    UniResponse,
    UploadConfig,
    UploadProgress,
    UploadResponse
} from './types'
import {InterceptorManagerImpl} from './interceptor'
import {buildFullURL, createError, mergeConfig} from './utils'

/**
 * UniRequest 核心实现类
 */
export class UniRequest implements UniRequestInstance {
    /** 默认配置 */
    private readonly defaultConfig: UniRequestUserConfig = {}

    /** 拦截器 */
    public interceptors = {
        request: new InterceptorManagerImpl<UniRequestInternalConfig>(),
        response: new InterceptorManagerImpl<UniResponse>()
    }

    /**
     * 构造函数
     * @param config 初始配置
     */
    constructor(config?: UniRequestUserConfig) {
        this.defaultConfig = config || {}
    }

    /**
     * 发起请求
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    async request<T = any>(config: UniRequestConfig): Promise<UniResponse<T>> {
        // 合并配置
        let mergedConfig = mergeConfig(this.defaultConfig, config)

        // 应用请求拦截器
        const requestInterceptorChain: Array<any> = []
        this.interceptors.request.forEach((interceptor) => {
            requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected)
        })

        // 执行请求拦截器
        let promise = Promise.resolve(mergedConfig)
        while (requestInterceptorChain.length) {
            promise = promise.then(requestInterceptorChain.shift(), requestInterceptorChain.shift())
        }

        // 发起实际请求
        let response: UniResponse<T>
        try {
            mergedConfig = await promise
            response = await this.dispatchRequest<T>(mergedConfig)
        } catch (error) {
            throw this.handleRequestError(error, mergedConfig)
        }

        // 应用响应拦截器
        const responseInterceptorChain: Array<any> = []
        this.interceptors.response.forEach((interceptor) => {
            responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected)
        })

        // 执行响应拦截器
        let responsePromise = Promise.resolve(response)
        while (responseInterceptorChain.length) {
            responsePromise = responsePromise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift())
        }

        return responsePromise
    }

    /**
     * GET 请求
     * @param url 请求地址
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    get<T = any>(url: string, config?: UniRequestConfig): Promise<UniResponse<T>> {
        return this.request<T>({...config, url, method: 'GET'})
    }

    /**
     * POST 请求
     * @param url 请求地址
     * @param data 请求数据
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    post<T = any>(url: string, data?: any, config?: UniRequestConfig): Promise<UniResponse<T>> {
        return this.request<T>({...config, url, method: 'POST', data})
    }

    /**
     * PUT 请求
     * @param url 请求地址
     * @param data 请求数据
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    put<T = any>(url: string, data?: any, config?: UniRequestConfig): Promise<UniResponse<T>> {
        return this.request<T>({...config, url, method: 'PUT', data})
    }

    /**
     * DELETE 请求
     * @param url 请求地址
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    delete<T = any>(url: string, config?: UniRequestConfig): Promise<UniResponse<T>> {
        return this.request<T>({...config, url, method: 'DELETE'})
    }

    /**
     * HEAD 请求
     * @param url 请求地址
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    head<T = any>(url: string, config?: UniRequestConfig): Promise<UniResponse<T>> {
        return this.request<T>({...config, url, method: 'HEAD'})
    }

    /**
     * OPTIONS 请求
     * @param url 请求地址
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    options<T = any>(url: string, config?: UniRequestConfig): Promise<UniResponse<T>> {
        return this.request<T>({...config, url, method: 'OPTIONS'})
    }

    /**
     * 上传文件
     * @param config 上传配置
     * @returns Promise<UploadResponse>
     */
    async upload(config: UploadConfig): Promise<UploadResponse> {
        const {filePath, name, formData, onProgress, ...restConfig} = config

        return new Promise((resolve, reject) => {
            const uploadTask = uni.uploadFile({
                url: buildFullURL(restConfig),
                filePath,
                name,
                formData,
                header: restConfig.headers,
                timeout: restConfig.timeout,
                success: (res) => {
                    resolve({
                        data: res.data,
                        statusCode: res.statusCode
                    })
                },
                fail: (error) => {
                    reject(createError(
                        error.errMsg || '上传失败',
                        restConfig,
                        (error as any).errno,
                        uploadTask,
                        undefined
                    ))
                }
            })

            // 监听上传进度
            if (onProgress) {
                uploadTask.onProgressUpdate((res) => {
                    const progress: UploadProgress = {
                        progress: res.progress,
                        totalBytesSent: res.totalBytesSent,
                        totalBytesExpectedToSend: res.totalBytesExpectedToSend
                    }
                    onProgress(progress)
                })
            }
        })
    }

    /**
     * 下载文件
     * @param config 下载配置
     * @returns Promise<DownloadResponse>
     */
    async download(config: DownloadConfig): Promise<DownloadResponse> {
        const {filePath, onProgress, ...restConfig} = config

        return new Promise((resolve, reject) => {
            const downloadTask = uni.downloadFile({
                url: buildFullURL(restConfig),
                filePath,
                header: restConfig.headers,
                timeout: restConfig.timeout,
                success: (res) => {
                    resolve({
                        tempFilePath: res.tempFilePath,
                        statusCode: res.statusCode
                    })
                },
                fail: (error) => {
                    reject(createError(
                        error.errMsg || '下载失败',
                        restConfig,
                        (error as any).errno,
                        downloadTask,
                        undefined
                    ))
                }
            })

            // 监听下载进度
            if (onProgress) {
                downloadTask.onProgressUpdate((res) => {
                    const progress: DownloadProgress = {
                        progress: res.progress,
                        totalBytesWritten: res.totalBytesWritten,
                        totalBytesExpectedToWrite: res.totalBytesExpectedToWrite
                    }
                    onProgress(progress)
                })
            }
        })
    }

    /**
     * 分发请求
     * @param config 请求配置
     * @returns Promise<UniResponse<T>>
     */
    private async dispatchRequest<T>(config: UniRequestConfig): Promise<UniResponse<T>> {
        return new Promise((resolve, reject) => {
            // 构建请求配置，只包含标准字段
            const requestConfig: UniApp.RequestOptions = {
                ...config,
                url: buildFullURL(config) || '',
                method: config.method || 'GET',
                header: config.headers,
                success: (response: UniApp.RequestSuccessCallbackResult) => {
                    const uniResponse: UniResponse<T> = {
                        data: response.data as T,
                        statusCode: response.statusCode,
                        header: response.header,
                        cookies: response.cookies
                    }
                    resolve(uniResponse)
                },
                fail: (error: UniApp.GeneralCallbackResult) => {
                    reject(createError(
                        error.errMsg || '请求失败',
                        config,
                        'NETWORK_ERROR',
                        undefined,
                        undefined
                    ))
                }
            }

            // 发起请求
            uni.request(requestConfig)
        })
    }

    /**
     * 处理请求错误
     * @param error 错误对象
     * @param config 请求配置
     * @returns UniRequestError
     */
    private handleRequestError(error: any, config: UniRequestConfig) {
        if (error.config) {
            return error
        }
        return createError(
            error.message || '请求失败',
            config,
            error.code,
            error.request,
            error.response
        )
    }
} 